;ORG     0x7c00;//程序启动地址 不改这里，改段地址

global _main

section .text 
_main:
 
		JMP _init ;//跳转到起始代码，后面一块被分区表占了
		times 0x78-($-$$) db 0x00;用这个通吃任何分区格式，浪费最多58个字节而已，反正这个启动代码的逻辑只是用来搞出下一步

;优化考量
;1.汇编语言也应该使用函数，不允许jmp到其它函数的标签
;2.隐藏地址		
	_init: ;初始化寄存器
		MOV		AX,CS		;MOV指令搬运值，AX=0  AX常用寄存器r，相对通用 ,CS当前段，取出
		MOV		ES,AX		;ES=AX
		MOV     AX,0x07c0	;用段地址的方式
		MOV		DS,AX		;DS=AX
		;ABCD 四个寄存器都是临时用，可以作为编译器的临时变量放置，用来传参，也可以用来收返回值，
		
		;init info
		MOV 	AX,strmsg
		MOV 	BX,DS
		CAll 	showmsg
		
		MOV		AX,1;//装载FAT表，1~10扇区
		MOV		BX,9
		MOV 	CX,0x0820;//段地址，注意 
		MOV		ES, CX;//装载地址
		call    readdisklba;(AX lba index,BX num,ES loadaddr)		
		
		MOV 	AX,19;//装载文件表，19~33 14个扇区
		MOV		BX,14;
		MOV		CX,0x0940
		MOV		ES,	CX;
		call    readdisklba;(AX lba index,BX num,ES loadaddr)		
		
		MOV		AX,0x9400
		call 	tryloadsys16;
	fin:
		HLT						; 让CPU停止，等待指令 sleep
		JMP		fin				; 无限循环

;//优化方向，函数是基础，call c style 函数 是自己推栈帧，清栈帧 和 用EAX 返回值
;//call 自己的内部函数，可以采用三个独立的栈空间
		;1.调用执行栈，也就是 SP，不碰他。然后可以让BP指向参数栈，调用栈
		;2.参数栈   一个线程一个，不会很大，分若干空间给他
		;3.计算栈   一个线程一个，不会很大，分若干空间给他

struc filetable
		.filename: 	resb 8 ; 8字节文件名
		;
		.extname:	resb 3;
		.attr		resb 1
					resb    10      ; 0x0C  10  保留
		.time:	  	resw  1;
		.date:      resw  1;
		.fatindex:  resw  1;
		.filelen:   resd  1;
endstruc

tryloadsys16: ;tryloadsys16(AX 地址)
		MOV CX,AX;
		
		MOV AX,0;
		MOV ES,AX;
	tryloadsys16.one:
		MOV SI,CX;
		
		;长文件名无视
		MOV AL,[ES:SI+filetable.attr]
		CMP AL,0x0F
		JZ tryloadsys16.next
		
	
		MOV EAX,[ES:SI]
		CMP AL,0
		JZ tryloadsys16.end
		
		;判断是不是16B.TOS
		CMP EAX,0x20423631
		;文件名前八字节31 36 42 20, 20 20 20 20  54 4F 53
		JNZ tryloadsys16.next
		MOV EAX,[ES:SI + filetable.extname-1]
		CMP EAX,0x534F5420
		JNZ tryloadsys16.next
						;print ES:strmsg
		;//find sys file	

		MOV 	AX,[ES:SI+filetable.fatindex]

		call loadsys16;
		
		ret;
		;16B
		;打印文件名
		;call showmsg
		; MOV AX,0
		; MOV ES,AX
		
		; MOV AX,SI
		; ADD AX,filetable.filename;
		
		; MOV BX,8
		
		; PUSH CX;//暂存CX，cx可能会被call 改变
		
		; call showmsg;
		
		; POP CX;//还原现场 cx 可能会被 call 改变
		
	tryloadsys16.next:
		
		ADD CX,0x20
		JMP tryloadsys16.one;
		
	tryloadsys16.end:;
		;print ES:strmsg		
		MOV 	AX,strnosys
		MOV 	BX,DS
		CAll 	showmsg
		ret;
		
loadsys16:;loadsys16(AX fatindex) 
		
		;0x0B00;//装载段
		MOV 	CX,0x0940;//段地址，装载位置


	loadsys16.one:;//CX 装载段，AX 扇区
		PUSH    CX;
		PUSH 	AX
		ADD		AX,31;//从31号扇区开始算
		MOV		BX,1
		MOV		ES, CX;//装载地址
		call readdisklba;
		
		POP 	AX;//取出fatindex
		MOV 	DX,0
		MOV 	BX,2
		DIV		BX ;AX= (DX:AX/BX) DX =(DX:AX %2) 
		MOV 	CX,DX;//乘法会影响DX，？？
		MOV 	BX,3
		MUL		BX
		
		;DL =0 
		CMP     CL,0
		JNZ		loadsys16.tag1
	loadsys16.tag0:	
		MOV 	BX,0x0820
		MOV 	ES,BX
		MOV 	SI,AX;
		MOV		AX,[ES:SI];
		MOV 	BX, 0xFFF;//最大值，&一下
		AND		AX,BX
		JMP 	loadsys16.next;
	loadsys16.tag1:
		;DL =1
		MOV 	BX,0x0820
		MOV 	ES,BX
		MOV 	SI,AX;
		MOV		AX,[ES:SI+1];
		SHR		AX,4
		
	loadsys16.next:	
		POP 	CX;
		ADD 	CX,0x20;
		CMP		AX,0x8ff;
		JB      loadsys16.one;
	loadsys16.finish:

		MOV 	BX,DS	
		MOV 	AX,strsys

		call showmsg;
		MOV 	AX,0x940;
		MOV  	ES,AX;
		MOV 	DS,AX;
		POP		BX;//把自己的两个调用栈弹出了
		POP		BX
		jmp 	0x940:0; 
		;ret;	彻底走了不再回头，不用回头看
		

readdisklba:	;AX LBA Index,BX Num,ES target LoadAddr
		;DEC     AX,1;
		MOV     DL,BL;暂存DL
		
		MOV		BL,18;AX /18
		DIV     BL ;除18 Al=other ah=扇区
		MOV		CL,AH  ; cl=扇区
		ADD 	CL,1;
		
		mov		AH,0;AX/2
		MOV		BL,2
		DIV		BL	;/2 AL =柱面 ah =磁头
		mov     CH,AL;	ch=柱面
		mov 	DH,AH;	dh=磁头

		MOV     AH,0x02;ah=2 读盘 al=17 扇区数量   
		MOV     AL,DL
		MOV		DL,0x00;DL=0软驱
		;MOV		AX,0x0217
		MOV		BX,0x0000;//偏移归零
		
		MOV     SI,0;//用SI来重试
	readdisklba.do:
		int 0x13			
		JC readdisklba.error;
	readdisklba.return:
		ret;
	readdisklba.error:
		ADD		SI,1;//用SI做5次尝试机制
		CMP		SI,5;
		JNZ		readdisklba.do;
		
		MOV AX,strreaderror;
		MOV BX,DS;
		;MOV ES,CX;
		;MOV BX,0
		CALL showmsg;
		ret;
		

		;chs 柱面磁头扇区

showmsg: ;void showmsg(BX:AX addr) ;AX =word 16bit AH AL 是他的8bit 高低位 EAX 是dword 32bit
		 MOV 		ES,BX;
		 MOV		SI,AX;//把参数1拉进SI
		 ; MOV 		CX,BX;
		 ; ;ifCX==0 CX=255
		 ; CMP		CX,0
		 ; JNC		showmsg.putloop
		 ; MOV		CX,0x1ff
	 showmsg.putloop:
		 MOV		AL,[ES:SI]		;AL = *SI  []的意思就是把SI作为地址，取这个内存位置的值，所有用到地址的时候默认地址都是
		 ;AL,[DS:SI]  从DS段读取
	    

		 ADD		SI,1			; 给SI加1	SI++
		 CMP		AL,0			; CMP 指令有隐藏的输出 Flags.ZF 会变成1
		 JZ			showmsg.return				; JE指令有隐藏的输入 if(Flags.ZF==1) JMP
		 
		
		 MOV		AH,0x0e			; 显示一个文字
		 MOV		BX,15			; 指定字符颜色
		 INT		0x10			; 调用显卡BIOS	INT隐藏的输入输出众多，根据调用的功能不同差距很大，前面的这些都是他的输入
			; ;INT10 输出文字使用四个输入 BH =0 AH =0x0e AL =字符 BX =颜色  显示文字
		 ; SUB		CX,1			; 限制长度逻辑
		 ; CMP		CX,0
		 ; JZ 		showmsg.return
		 JMP		showmsg.putloop
	 showmsg.return:		
	 
		 RET;
;showmsg end

;所以c语言用地址的形式访问参数，而不是取出来，返回值用EAX处理
;栈的清理由call的一方处理，你怎么push进去的，就还怎么pop掉

section .data
	strmsg:
		db	'TTOS INIT...',0x0d,0x0a,0x00
	strsys:
		db	'LOAD SYS FILE.',0x0d,0x0a,0x00	
	strnosys:
		db	'NO SYS FILE.',0x0d,0x0a,0x00 
	strreaderror:
		db  'READ DISK ERROR',0x0d,0x0a,0x00